﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vds2465Telegrams;

namespace Vds2465Parser
{
	internal static class GenParse_UserData
	{
		#region constants

		internal const byte MinLength = 2;
		internal const int PositionOfUserDataType = 1;

		/// <summary>
		/// The header includes the SL byte (length of real user data) and the UserDataType byte
		/// </summary>
		internal const int HeaderLength = 2;

		#endregion constants

		#region delegates

		internal delegate bool Delegate_ParseBytes(byte[] userDataBytes, out UserData userData, out int consumedBytes);

		#endregion delegates

		#region internal methods

		internal static byte[] GenerateBytes4UserDataColl(UserDataColl userDataColl)
		{
			IList<byte> userDataBytesList = new List<byte>();

			for (int index=0; index<userDataColl.Count; index++)
			{
				UserData userData;
				bool bRetVal = userDataColl.GetByIndex(index, out userData);
				if (bRetVal)
				{
					byte[] userDataBytesSingle = GenerateBytes4UserData(userData);
					if (userDataBytesSingle != null)
					{
						for (int i = 0; i < userDataBytesSingle.Length; i++)
						{
							// TODO check, improve
							userDataBytesList.Add(userDataBytesSingle[i]);
						}
					}
				}
			}

			byte[] userDataBytes = userDataBytesList.ToArray();			
			return userDataBytes;
		}

		internal static UserDataColl ParseBytes4UserDataColl(byte[] userDataBytes)
		{
			UserDataColl userDataColl = new UserDataColl();

			if (userDataBytes != null)
			{
				if (userDataBytes.Length >= MinLength)
				{
					// initialize
					byte[] remainingUserDataBytes = new byte[userDataBytes.Length];
					userDataBytes.CopyTo(remainingUserDataBytes, 0);

					while (remainingUserDataBytes.Length > 0)
					{
						byte userDataType = remainingUserDataBytes[PositionOfUserDataType];
						switch (userDataType)
						{
							case UserData.UserData01Priority:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_01_Priority.ParseBytes);
								break;
							case UserData.UserData02StatusChangeReport2Confirm:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_02_StatusChangeReport2Confirm.ParseBytes);
								break;
							case UserData.UserData03StatusChangeConfirm:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_03_StatusChangeConfirm.ParseBytes);
								break;
							case UserData.UserData04StatusChangeReport:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_04_StatusChangeReport.ParseBytes);
								break;
                            case UserData.UserData10Query:
                                remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_10_Query.ParseBytes);
                                break;
                            case UserData.UserData11Error:
                                remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_11_Error.ParseBytes);
                                break;
                            case UserData.UserData20State:
                                remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_20_State.ParseBytes);
                                break;
                            case UserData.UserData24BlockState:
                                remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_24_BlockState.ParseBytes);
                                break;
							case UserData.UserData26BlockStateAll:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_26_BlockStateAll.ParseBytes);
                                break;
							case UserData.UserData30Measurements:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_30_Measurements.ParseBytes);
								break;
							case UserData.UserData35GPSCoordinates:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_35_GPSCoordinates.ParseBytes);
								break;
							case UserData.UserData40Test:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_40_Test.ParseBytes);
								break;
							case UserData.UserData41TestConfirm:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_41_TestConfirm.ParseBytes);
								break;
							case UserData.UserData42SecurityCode:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_42_SecurityCode.ParseBytes);
								break;
							case UserData.UserData44History:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_44_History.ParseBytes);
								break;
							case UserData.UserData48ConnectionPath:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_48_ConnectionPath.ParseBytes);
								break;
							case UserData.UserData50DateAndTime:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_50_DateAndTime.ParseBytes);
								break;
							case UserData.UserData51ManufacturerId:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_51_ManufacturerId.ParseBytes);								
								break;
							case UserData.UserData52CommunicationAddressS2:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_52_CommunicationAddressS2.ParseBytes);
								break;
							case UserData.UserData53CommunicationAddressS3:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_53_CommunicationAddressS3.ParseBytes);
								break;
							case UserData.UserData54AsciiChars:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_54_AsciiCharacters.ParseBytes);
								break;
                            case UserData.UserData55SupportedUserDataTypes:
                                remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_55_SupportedUserDataTypes.ParseBytes);
								break;
							case UserData.UserData56IdentificationNumber:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_56_IdentificationNumber.ParseBytes);
								break;
							case UserData.UserData60NetStatus:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_60_NetStatus.ParseBytes);
								break;
							case UserData.UserData61TransportServiceType:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_61_TransportServiceType.ParseBytes);
								break;
							case UserData.UserData62SourceNetAddress:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_62_SourceNetAddress.ParseBytes);
								break;
							case UserData.UserData70DataBlock:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_70_DataBlock.ParseBytes);
								break;
							case UserData.UserData71DataBlockFinal:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_71_DataBlockFinal.ParseBytes);
								break;
							case UserData.UserData72DataBlockValid:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_72_DataBlockValid.ParseBytes);
								break;
							case UserData.UserData73TelegramCounter:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_73_TelegramCounter.ParseBytes);
								break;
							case UserData.UserData74Container:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_74_Container.ParseBytes);
								break;
							case UserData.UserDataFFConnectionNotNeeded:
								remainingUserDataBytes = ParseBytes4SingleUserData(remainingUserDataBytes, userDataColl, GenParse_UserData_FF_ConnectionNotNeeded.ParseBytes);
								break;
							default:
								// TODO handle error (unknown userDataType)
								throw new InvalidOperationException("Unknown UserDataType");
						}
					}
				}
				else
				{
					// TODO handle error
					return userDataColl;
				}
			}

			// TODO handle error
			return userDataColl;
		}

		#endregion internal methods

		#region private methods

		private static byte[] GenerateBytes4UserData(UserData userData)
		{
			byte[] userDataBytes = null;
			switch (userData.DataType)
			{
				case UserData.UserData01Priority:
					userDataBytes = GenParse_UserData_01_Priority.GenerateBytes(userData);
					break;
				case UserData.UserData02StatusChangeReport2Confirm:
					userDataBytes = GenParse_UserData_02_StatusChangeReport2Confirm.GenerateBytes(userData);
					break;
				case UserData.UserData03StatusChangeConfirm:
					userDataBytes = GenParse_UserData_03_StatusChangeConfirm.GenerateBytes(userData);
					break;
				case UserData.UserData04StatusChangeReport:
					userDataBytes = GenParse_UserData_04_StatusChangeReport.GenerateBytes(userData);
					break;
                case UserData.UserData10Query:
                    userDataBytes = GenParse_UserData_10_Query.GenerateBytes(userData);
					break;
                case UserData.UserData11Error:
                    userDataBytes = GenParse_UserData_11_Error.GenerateBytes(userData);
                    break;
                case UserData.UserData20State:
                    userDataBytes = GenParse_UserData_20_State.GenerateBytes(userData);
                    break;
                case UserData.UserData24BlockState:
                    userDataBytes = GenParse_UserData_24_BlockState.GenerateBytes(userData);
                    break;
				case UserData.UserData26BlockStateAll:
					userDataBytes = GenParse_UserData_26_BlockStateAll.GenerateBytes(userData);
					break;
				case UserData.UserData30Measurements:
					userDataBytes = GenParse_UserData_30_Measurements.GenerateBytes(userData);
					break;
				case UserData.UserData35GPSCoordinates:
					userDataBytes = GenParse_UserData_35_GPSCoordinates.GenerateBytes(userData);
					break;
				case UserData.UserData40Test:
					userDataBytes = GenParse_UserData_40_Test.GenerateBytes(userData);
					break;
				case UserData.UserData41TestConfirm:
					userDataBytes = GenParse_UserData_41_TestConfirm.GenerateBytes(userData);
					break;
				case UserData.UserData42SecurityCode:
					userDataBytes = GenParse_UserData_42_SecurityCode.GenerateBytes(userData);
					break;
				case UserData.UserData44History:
					userDataBytes = GenParse_UserData_44_History.GenerateBytes(userData);
					break;
				case UserData.UserData48ConnectionPath:
					userDataBytes = GenParse_UserData_48_ConnectionPath.GenerateBytes(userData);
					break;
				case UserData.UserData50DateAndTime:
					userDataBytes = GenParse_UserData_50_DateAndTime.GenerateBytes(userData);
					break;
				case UserData.UserData51ManufacturerId:
					userDataBytes = GenParse_UserData_51_ManufacturerId.GenerateBytes(userData);
					break;
				case UserData.UserData52CommunicationAddressS2:
					userDataBytes = GenParse_UserData_52_CommunicationAddressS2.GenerateBytes(userData);
					break;
				case UserData.UserData53CommunicationAddressS3:
					userDataBytes = GenParse_UserData_53_CommunicationAddressS3.GenerateBytes(userData);
					break;
				case UserData.UserData54AsciiChars:
					userDataBytes = GenParse_UserData_54_AsciiCharacters.GenerateBytes(userData);
					break;
                case UserData.UserData55SupportedUserDataTypes:
                    userDataBytes = GenParse_UserData_55_SupportedUserDataTypes.GenerateBytes(userData);
                    break;
				case UserData.UserData56IdentificationNumber:
					userDataBytes = GenParse_UserData_56_IdentificationNumber.GenerateBytes(userData);
					break;
				case UserData.UserData60NetStatus:
					userDataBytes = GenParse_UserData_60_NetStatus.GenerateBytes(userData);
					break;
				case UserData.UserData61TransportServiceType:
					userDataBytes = GenParse_UserData_61_TransportServiceType.GenerateBytes(userData);
					break;
				case UserData.UserData62SourceNetAddress:
					userDataBytes = GenParse_UserData_62_SourceNetAddress.GenerateBytes(userData);
					break;
				case UserData.UserData70DataBlock:
					userDataBytes = GenParse_UserData_70_DataBlock.GenerateBytes(userData);
					break;
				case UserData.UserData71DataBlockFinal:
					userDataBytes = GenParse_UserData_71_DataBlockFinal.GenerateBytes(userData);
					break;
				case UserData.UserData72DataBlockValid:
					userDataBytes = GenParse_UserData_72_DataBlockValid.GenerateBytes(userData);
					break;
				case UserData.UserData73TelegramCounter:
					userDataBytes = GenParse_UserData_73_TelegramCounter.GenerateBytes(userData);
					break;
				case UserData.UserData74Container:
					userDataBytes = GenParse_UserData_74_Container.GenerateBytes(userData);
					break;
				case UserData.UserDataFFConnectionNotNeeded:
					userDataBytes = GenParse_UserData_FF_ConnectionNotNeeded.GenerateBytes(userData);
					break;
				default:
					// TODO handle error
					break;
			}

			return userDataBytes;
		}

		private static byte[] ParseBytes4SingleUserData(byte[] userDataBytes, UserDataColl userDataColl, Delegate_ParseBytes parseBytes)
		{
			UserData userData;
			int consumedBytes;
			bool bRetVal = parseBytes(userDataBytes, out userData, out consumedBytes);
			if (bRetVal)
			{
				userDataColl.Add(userData);
				int nrOfRemainingUserDataBytes = userDataBytes.Length - consumedBytes;
				if (nrOfRemainingUserDataBytes >= 0)
				{
					byte[] remainingUserDataBytes = new byte[nrOfRemainingUserDataBytes];
					if (nrOfRemainingUserDataBytes > 0)
					{
						for (int index = 0; index < nrOfRemainingUserDataBytes; index++)
						{
							remainingUserDataBytes[index] = userDataBytes[consumedBytes + index];
						}						
					}
					return remainingUserDataBytes;
				}
				else
				{
					// TODO handle error (negative number...)
					throw new InvalidOperationException("negative number");
				}
			}
			else
			{
				// TODO handle error (unknown userDataType)
				throw new InvalidOperationException("Unknown UserDataType");
			}
		}

		#endregion private methods
	}
}
